﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace Gmantis.Controls.Chart
{
    [TemplatePart(Name = "ScrollBar", Type = typeof(ScrollBar))]
    public class AxisScrollBar : ContentControl, IAxisScrollBar
    {
        // Fields
        private Axis _axis;
        private ScrollBar _sb;
        private double _scale = 1.0;
        private double _value;
        private bool notify = true;
        internal const string ScrollBarElementName = "ScrollBar";

        // Events
        public event AxisRangeChangedEventHandler AxisRangeChanged;

        // Methods
        public AxisScrollBar()
        {
            base.DefaultStyleKey = typeof(AxisScrollBar);
        }

        private void _axis_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.notify = false;
            try
            {
                if (e.PropertyName == "Value")
                {
                    this.AxisValue = this.Axis.Value;
                }
                else if (e.PropertyName == "Scale")
                {
                    this.AxisScale = this.Axis.Scale;
                }
            }
            finally
            {
                this.notify = true;
            }
        }

        private void InitScrollBar()
        {
            RoutedPropertyChangedEventHandler<double> handler = null;
            if (this._sb != null)
            {
                this.UpdateScale();
                this.UpdateValue();
                if (handler == null)
                {
                    handler = (s, e) => this.OnAxisRangeChanged();
                }
                this._sb.ValueChanged += handler;
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this._sb = base.GetTemplateChild("ScrollBar") as ScrollBar;
            this.InitScrollBar();
        }

        private void OnAxisRangeChanged()
        {
            if (this.notify && (this.AxisRangeChanged != null))
            {
                this.AxisRangeChanged(this, new AxisRangeChangedEventArgs(this.AxisValue, this.AxisScale));
            }
        }

        private void UpdateScale()
        {
            if (this._scale == 1.0)
            {
                this._sb.ViewportSize = 10000.0;
                this._sb.LargeChange = 1.0;
                this._sb.SmallChange = 1.0;
            }
            else
            {
                this._sb.ViewportSize = (1.0 / (1.0 - this._scale)) - 1.0;
                this._sb.LargeChange = this._sb.ViewportSize;
                this._sb.SmallChange = 0.5 * this._sb.ViewportSize;
            }
        }

        private void UpdateValue()
        {
            if ((this.Axis != null) && (this.Axis.AxisType == AxisType.Y))
            {
                this._sb.Value = 1.0 - this._value;
            }
            else
            {
                this._sb.Value = this._value;
            }
        }

        // Properties
        public Axis Axis
        {
            get
            {
                return this._axis;
            }
            set
            {
                if (this._axis != value)
                {
                    if (this._axis != null)
                    {
                        this._axis.PropertyChanged -= new PropertyChangedEventHandler(this._axis_PropertyChanged);
                    }
                    this._axis = value;
                    if (this._axis != null)
                    {
                        this._axis.PropertyChanged += new PropertyChangedEventHandler(this._axis_PropertyChanged);
                        this.notify = false;
                        this.AxisScale = this._axis.Scale;
                        this.AxisValue = this._axis.Value;
                        this.notify = true;
                    }
                }
            }
        }

        private double AxisScale
        {
            get
            {
                return this._scale;
            }
            set
            {
                this._scale = value;
                if (this._sb != null)
                {
                    this.UpdateScale();
                }
                this.OnAxisRangeChanged();
            }
        }

        private double AxisValue
        {
            get
            {
                if (this._sb == null)
                {
                    return this._value;
                }
                if ((this.Axis != null) && (this.Axis.AxisType == AxisType.Y))
                {
                    return (1.0 - this._sb.Value);
                }
                return this._sb.Value;
            }
            set
            {
                this._value = value;
                if (this._sb != null)
                {
                    this.UpdateValue();
                }
            }
        }

        public virtual Thickness ScrollBarMargin
        {
            get
            {
                if (this._sb != null)
                {
                    RepeatButton button = Utils.FindVisualChildByName<RepeatButton>(this._sb, "HorizontalSmallIncrease");
                    if (button != null)
                    {
                        return new Thickness(-(button.Width + button.Margin.Left), 0.0, -(button.Width + button.Margin.Right), 0.0);
                    }
                }
                return new Thickness(0.0);
            }
        }

        public AxisScrollBarPosition ScrollBarPosition { get; set; }
    }
}
